package cn.dengta.webapp.trade.service;

import java.math.BigDecimal;
import java.time.Duration;
import java.util.*;
import javax.annotation.Nonnull;
import javax.annotation.Resource;

import cn.dengta.common.model.*;
import cn.dengta.common.web.Page;
import cn.dengta.context.model.RedisKey;
import cn.dengta.context.model.WesKit;
import cn.dengta.webapp.base.service.BaseServiceImpl;
import cn.dengta.webapp.base.util.MybatisUtil;
import cn.dengta.webapp.trade.dao.UserIntegralLogDao;
import cn.dengta.webapp.trade.dto.*;
import cn.dengta.webapp.trade.dto.common.RecommendWhite;
import cn.dengta.webapp.trade.entity.*;
import cn.dengta.webapp.trade.form.UserIntegralLogQuery;
import cn.dengta.webapp.trade.model.Flow;
import cn.dengta.webapp.trade.model.TradeType;
import cn.dengta.webapp.trade.vo.IntegralDaySumVO;
import cn.dengta.webapp.user.model.BizType;
import cn.dengta.webapp.user.model.Gender;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import me.codeplayer.util.*;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;

import static cn.dengta.webapp.trade.entity.UserIntegral.USER_ID;
import static cn.dengta.webapp.trade.entity.UserIntegralLog.*;
import static cn.dengta.webapp.trade.model.TradeType.*;

/**
 * 积分流水记录表 服务实现类
 *
 * @author yuqidi
 * @since 2022-03-12
 */
@Service
public class UserIntegralLogService extends BaseServiceImpl<UserIntegralLog, UserIntegralLogDao, Long> {

	@Resource
	private StringRedisTemplate stringRedisTemplate;

	public List<UserIntegralLog> findFrozenIncomeGroup(Date endTime) {
		return baseDao.findFrozenIncomeGroup(ArrayUtil.getInSQL(null, new TradeType[] { INTEGRAL_GAIN, INTEGRAL_REWARD_USER, INTEGRAL_REWARD_AGENT, INTEGRAL_GAIN_EXTRACT_USER, INTEGRAL_REWARD_SPEED_DATING }, true, true).toString(), endTime);
	}

	public int makeIncomeActive(Date endTime) {
		return baseDao.makeIncomeActive(ArrayUtil.getInSQL(null, new TradeType[] { INTEGRAL_GAIN, INTEGRAL_REWARD_USER, INTEGRAL_REWARD_AGENT, INTEGRAL_GAIN_EXTRACT_USER, INTEGRAL_REWARD_SPEED_DATING }, true, true).toString(), endTime);
	}

	public Page<UserIntegralLog> findFrontPage(Page<UserIntegralLog> page, UserIntegralLogQuery query, TimeInterval addTimeInterval) {
		QueryWrapper<UserIntegralLog> wrapper = new QueryWrapper<>();
		if (query != null) {
			wrapper.eq(query.getUserId() != null, USER_ID, query.getUserId())
					.eq(query.getFlow() != null, FLOW, query.getFlow())
					.eq(query.getSide() != null, SIDE, query.getSide())
					.eq(query.getHandleStatus() != null, HANDLE_STATUS, query.getHandleStatus())
					.eq(StringUtil.notEmpty(query.getTradeType()), TRADE_TYPE, query.getTradeType());
			MybatisUtil.wrapperInOrEq(wrapper, TRADE_TYPE, query.getTradeTypes());
		}
		MybatisUtil.wrapperInterval(wrapper, null, addTimeInterval);
		wrapper.orderByDesc(ID);
		return baseDao.selectPage(page, wrapper);
	}

	public Page<UserIntegralLog> findPage(Page<UserIntegralLog> page, UserIntegralLogQuery query, TimeInterval addTimeInterval, TradeType... tradeTypes) {
		return baseDao.findUserPage(page, query, addTimeInterval, tradeTypes);
	}

	public List<IntegralDaySumVO> findDaySumTotal(UserIntegralLogQuery query, TimeInterval addTimeInterval) {
		QueryWrapper<UserIntegralLog> wrapper = new QueryWrapper<>();
		if (Flow.INCOME.eq(query.getFlow())) {
			wrapper.eq(query.getFlow() != null, FLOW, query.getFlow());
		}
		if (Flow.CONSUME.eq(query.getFlow())) {
			wrapper.in(query.getFlow() != null, FLOW, Flow.NONE.value, Flow.CONSUME.value);
		}
		wrapper.eq(query.getUserId() != null, USER_ID, query.getUserId());
		wrapper.eq(query.getToUserId() != null, TO_USER_ID, query.getToUserId());
		wrapper.eq(StringUtil.notEmpty(query.getTradeType()), TRADE_TYPE, query.getTradeType());
		MybatisUtil.wrapperInterval(wrapper, ADD_TIME, addTimeInterval);
		return baseDao.findDaySumTotal(wrapper);
	}

	public List<UserIntegralLog> find(Long userId, Integer flow, Integer side, String assocType, Long assocId, TradeType[] tradeTypes, TimeInterval addTimeInterval) {
		return selectList(popWrapper(userId, flow, side, assocType, assocId, tradeTypes, null, addTimeInterval));
	}

	public List<UserIntegralLog> find(Long userId, TimeInterval addTimeInterval, Integer handleStatus, TradeType... tradeTypes) {
		return selectList(popWrapper(userId, null, null, null, null, tradeTypes, handleStatus, addTimeInterval));
	}

	public int count(Long userId, TimeInterval addTimeInterval, TradeType... tradeTypes) {
		return NumberUtil.getInt(count(popWrapper(userId, null, null, null, null, tradeTypes, null, addTimeInterval)), 0);
	}

	/***
	 * 查询指定用户、指定交易时间段、指定交易类型(可变参数)的交易数据键/值对(交易类型/交易金额)即可
	 */
	public List<UserIntegralLogSumDTO> getSumOfTradeAmountGroupByType(Long[] userIds, Integer flow, TimeInterval addTimeInterval, String groupAttr, String selectKey, @Nonnull TradeType... tradeTypes) {
		return getSumOfTradeAmountGroupByType(userIds, flow, addTimeInterval, groupAttr, selectKey, null, tradeTypes);
	}

	public List<UserIntegralLogSumDTO> getSumOfTradeAmountGroupByType(Long[] userIds, Integer flow, TimeInterval addTimeInterval, String groupAttr, String selectKey, BizType bizType, @Nonnull TradeType... tradeTypes) {
		UserIntegralLogQuery query = new UserIntegralLogQuery()
				.setBizType(bizType)
				.setFlow(flow);
		query.userIds = userIds;
		query.groupAttr = groupAttr;
		query.selectKey = selectKey;
		return baseDao.getSumOfTradeAmountGroupByType(query, addTimeInterval, tradeTypes);
	}

	/***
	 * 查询指定用户、指定交易时间段、指定交易类型(可变参数)的推广员交易数据键
	 */
	public List<UserIntegralLogSumDTO> findSumOfAgentInfo(Long[] agentIds, Long[] userIds, Integer flow, TimeInterval addTimeInterval, String groupAttr, String selectKey, boolean userFromOrTo, Integer level, @Nonnull TradeType... tradeTypes) {
		UserIntegralLogQuery query = new UserIntegralLogQuery()
				.setAgentIds(agentIds)
				.setAgentLevel(level)
				.setFlow(flow);
		query.userIds = userIds;
		query.groupAttr = groupAttr;
		query.selectKey = selectKey;
		return baseDao.findSumOfAgent(query, addTimeInterval, false, userFromOrTo, false, tradeTypes);
	}

	public List<UserIntegralLogSumDTO> findSumOfAgentInfo(UserIntegralLogQuery query, Long[] agentIds, Long[] userIds, Integer flow, TimeInterval addTimeInterval, String groupAttr, String selectKey, boolean userFromOrTo, Integer level, @Nonnull TradeType... tradeTypes) {
		if (query == null) {
			query = new UserIntegralLogQuery();
		}
		query.setAgentIds(agentIds)
				.setAgentLevel(level)
				.setFlow(flow);
		query.userIds = userIds;
		query.groupAttr = groupAttr;
		query.selectKey = selectKey;
		return baseDao.findSumOfAgent(query, addTimeInterval, false, userFromOrTo, false, tradeTypes);
	}

	public List<UserIntegralLogSumDTO> findSumOfAgentInfo(Long[] agentIds, Integer flow, TimeInterval addTimeInterval, String groupAttr, String selectKey, @Nonnull TradeType... tradeTypes) {
		return findSumOfAgentInfo(agentIds, null, flow, addTimeInterval, groupAttr, selectKey, true, null, tradeTypes);
	}

	public Page<UserIntegralLogSumDTO> findPageSumOfAgent(Page<UserIntegralLog> page, Long[] agentIds, Integer flow, TimeInterval addTimeInterval, String groupAttr, String selectKey, Integer level, @Nonnull TradeType... tradeTypes) {
		UserIntegralLogQuery query = new UserIntegralLogQuery()
				.setAgentIds(agentIds)
				.setAgentLevel(level)
				.setFlow(flow);
		query.groupAttr = groupAttr;
		query.selectKey = selectKey;
		return baseDao.findPageSumOfAgent(page, query, addTimeInterval, false, tradeTypes);
	}

	/***
	 * 查询指定用户、指定交易时间段、指定交易类型(可变参数)的推广员钻石流水
	 */
	public List<UserIntegralLogSumDTO> findAgentSumForCoin(Long[] agentIds, Long[] userIds, Integer flow, TimeInterval addTimeInterval, String groupAttr, String selectKey, boolean showAll, Integer agentLevel, @Nonnull TradeType... tradeTypes) {
		UserIntegralLogQuery query = new UserIntegralLogQuery()
				.setAgentIds(agentIds)
				.setAgentLevel(agentLevel)
				.setFlow(flow);
		query.userIds = userIds;
		query.groupAttr = groupAttr;
		query.selectKey = selectKey;
		return baseDao.findSumOfAgent(query, addTimeInterval, showAll, true, true, tradeTypes);
	}

	/**
	 * 根据查询条件查询满足的数据
	 */
	public List<UserIntegralLog> findByGender(TimeInterval addTimeInterval, Gender gender, TradeType... tradeTypes) {
		return baseDao.findByGender(addTimeInterval, gender, null, tradeTypes);
	}

	public List<UserIntegralLog> findByGender(TimeInterval addTimeInterval, Gender gender, Long[] liveIds, TradeType... tradeTypes) {
		return baseDao.findByGender(addTimeInterval, gender, liveIds, tradeTypes);
	}

	public List<Long> findUserIdByGender(TimeInterval addTimeInterval, Gender gender, Long[] liveIds, Boolean forLive, TradeType... tradeTypes) {
		return baseDao.findUserIdByGender(addTimeInterval, gender, liveIds, forLive, tradeTypes);
	}

	public List<UserIntegralLogSumDTO> findChannelIncome(TimeInterval addTimeInterval, Gender gender, UserIntegralLogQuery query, @Nonnull TradeType... tradeTypes) {
		return baseDao.findChannelIncome(addTimeInterval, gender, query, tradeTypes);
	}

	/**
	 * 统计数量
	 *
	 * @param flow 资金流向：1=收入；0=不变；-1=支出
	 * @param side 交易方：1=from；2=to
	 */
	public int count(Long userId, Integer flow, Integer side, String assocType, Long assocId, TimeInterval addTimeInterval, TradeType... tradeTypes) {
		return NumberUtil.getInt(count(popWrapper(userId, flow, side, assocType, assocId, tradeTypes, null, addTimeInterval)), 0);
	}

	private QueryWrapper<UserIntegralLog> popWrapper(Long userId, Integer flow, Integer side, String assocType, Long assocId, TradeType[] tradeTypes, Integer handleStatus, TimeInterval addTimeInterval) {
		QueryWrapper<UserIntegralLog> wrapper = new QueryWrapper<>();
		wrapper.eq(userId != null, USER_ID, userId)
				.eq(flow != null, FLOW, flow)
				.eq(side != null, SIDE, side)
				.eq(handleStatus != null, HANDLE_STATUS, handleStatus)
				.eq(assocId != null, ASSOC_ID, assocId)
				.eq(StringUtil.notEmpty(assocType), ASSOC_TYPE, assocType);
		MybatisUtil.wrapperInOrEq(wrapper, TRADE_TYPE, tradeTypes);
		MybatisUtil.wrapperInterval(wrapper, ADD_TIME, addTimeInterval);
		return wrapper;
	}

	public Page<UserIntegralLog> findPageForAgent(Page<UserIntegralLog> page, UserIntegralLogQuery query, TimeInterval addTimeInterval) {
		QueryWrapper<UserIntegralLog> wrapper = new QueryWrapper<>();
		final String prefix = "log.";
		if (query != null) {
			wrapper.eq(query.getUserId() != null, prefix + USER_ID, query.getUserId())
					.eq(query.getFlow() != null, prefix + FLOW, query.getFlow())
					.eq(query.getSide() != null, prefix + SIDE, query.getSide())
					.ge(query.getHandleStatus() != null, prefix + HANDLE_STATUS, query.getHandleStatus())
					.eq(StringUtil.notEmpty(query.getTradeType()), prefix + TRADE_TYPE, query.getTradeType())
					.like(StringUtil.notEmpty(query.getUserNickname()), "u.nickname", query.getUserNickname());
			MybatisUtil.wrapperInOrEq(wrapper, TRADE_TYPE, query.getTradeTypes());
			Long agentId = query.getAgentId();
			wrapper.and(agentId == null, c -> c.isNotNull(prefix + AGENT_ID).or().isNotNull(prefix + AGENT2_ID));
			if (agentId != null) {
				wrapper.eq("u.agent_id", agentId);
				wrapper.gtSql(prefix + ADD_TIME, "u.agent_join_time");
				wrapper.and(c -> c.eq(prefix + AGENT_ID, agentId).or().eq(prefix + AGENT2_ID, agentId));
			}
			if (Bean.isValidId(query.getInviteEmpId())) {
				wrapper.inSql("u.agent_id", "SELECT a.id FROM dt_agent a WHERE a.status > 0 AND a.invite_emp_id =" + query.getInviteEmpId());
			}
		}
		MybatisUtil.wrapperInterval(wrapper, prefix + ADD_TIME, addTimeInterval);
		return baseDao.findPage(page, wrapper);
	}

	/** 获取指定时间的用户收益(不包含奖励) */
	public List<UserIntegralLog> findAgentSumByTime(Date startTime, Date endTime, String key) {
		return baseDao.findAgentSumByTime(startTime, endTime, key);
	}

	/** 关联推广员奖励信息 */
	public void relationAgentRewardId(String userIds, Long agentRewardId, String liveId, Date startTime, Date endTime) {
		baseDao.relationAgentRewardId(userIds, agentRewardId, liveId, startTime, endTime);
	}

	/** 隐藏被冻结的奖励 */
	public void freezeAgentReward(String userIds, String liveId, Date startTime, Date endTime) {
		baseDao.freezeAgentReward(userIds, liveId, startTime, endTime);
	}

	public List<UserIntegralLog> getCountByAddTime(WesKit wesKit, Long[] userIds, Gender gender, TimeInterval addTimeInterval, BigDecimal minIntegral, TradeType... tradeTypes) {
		return baseDao.getCountByAddTime(wesKit, userIds, gender, addTimeInterval, minIntegral, tradeTypes);
	}

	public List<UserIntegral> findIncomeSum(Long[] userIds, Date minAddTime) {
		QueryWrapper<UserIntegralLog> wrapper = new QueryWrapper<UserIntegralLog>()
				.ge("add_time", minAddTime)
				.in("user_id", (Object[]) userIds)
				.eq("trade_type", TradeType.INTEGRAL_GAIN.name());
		return baseDao.findIncomeSum(wrapper);
	}

	public BigDecimal getTotalIntegralByLiveId(Long liveId) {
		return baseDao.getTotalIntegralByLiveId(liveId);
	}

	public UserIntegralLog getSumOfTradeAmount(Long userId, Long toUserId, Integer flow, Boolean fromLive, String assocType, Long assocId, TimeInterval addTimeInterval, TradeType... tradeTypes) {
		QueryWrapper<UserIntegralLog> wrapper = new QueryWrapper<UserIntegralLog>()
				.eq(userId != null, UserIntegralLog.USER_ID, userId)
				.eq(toUserId != null, TO_USER_ID, toUserId)
				.in(tradeTypes != null, TRADE_TYPE, (Object[]) tradeTypes)
				.eq(assocId != null, ASSOC_ID, assocId)
				.eq(StringUtil.notEmpty(assocType), ASSOC_TYPE, assocType)
				.eq(flow != null, FLOW, flow);
		if (fromLive != null) {
			if (fromLive) {
				wrapper.isNotNull(LIVE_ID);
			} else {
				wrapper.isNull(LIVE_ID);
			}
		}
		MybatisUtil.wrapperInterval(wrapper, ADD_TIME, addTimeInterval);
		wrapper.select("IFNULL(SUM(trade_amount),0) tradeAmount");
		return baseDao.selectOne(wrapper);
	}

	public void updateUseStatus(Long id) {
		if (Cmp.gt(id, 0)) {
			this.update(new UpdateWrapper<UserIntegralLog>().set(USE_STATUS, Status.YES.value)
					.eq(ID, id).eq(USE_STATUS, Status.NO.value));
		}
	}

	public Page<UserIntegralLog> findLogPage(Page<UserIntegralLog> page, UserIntegralLogQuery query, TimeInterval timeInterval) {
		QueryWrapper<UserIntegralLog> qw = new QueryWrapper<>();
		qw.eq(query.getUserId() != null, USER_ID, query.getUserId());
		if (Flow.INCOME.eq(query.getFlow())) {
			qw.eq(FLOW, Flow.INCOME.value);
		}
		if (Flow.CONSUME.eq(query.getFlow())) {
			qw.in(FLOW, Flow.CONSUME.value, Flow.NONE.value);
		}
		MybatisUtil.wrapperInterval(qw, ADD_TIME, timeInterval);
		qw.orderByDesc(ID);
		return selectPage(page, qw);
	}

	public List<UserIntegralLog> findByIntervalSumAmount(Date minTime, int limit) {
		final QueryWrapper<UserIntegralLog> qw = new QueryWrapper<>();
		qw.select("user_id,SUM(trade_amount) AS trade_amount");
		qw.ge(ADD_TIME, minTime);
		qw.eq(FLOW, Flow.INCOME.value);
		qw.groupBy(USER_ID);
		qw.orderByDesc("trade_amount");
		qw.last("LIMIT " + limit);
		return selectList(qw);
	}

	public UserIntegralLog findByIntervalSumAmount(Date minTime, Long userId) {
		final QueryWrapper<UserIntegralLog> qw = new QueryWrapper<>();
		qw.select("SUM(trade_amount) AS trade_amount");
		qw.ge(ADD_TIME, minTime);
		qw.eq(FLOW, Flow.INCOME.value);
		qw.eq(USER_ID, userId);
		return selectOne(qw);
	}

	/** 用户最后几天收入排名 */
	public void userLastDayIncomeRanking() {
		if (Boolean.FALSE.equals(stringRedisTemplate.opsForValue().setIfAbsent("USER_INCOME_SUM_LAST_DAY_RANKING_KEY", "1", Duration.ofMinutes(2)))) {
			return;
		}
		final RecommendWhite white = ShareCommonConfig.buildWhite();
		final EasyDate now = new EasyDate().addDay(-white.getOld().getDayIncome());
		final List<UserIntegralLog> list = findByIntervalSumAmount(now.toDate(), white.getOld().getRanking());
		stringRedisTemplate.delete(RedisKey.USER_INCOME_LAST_DAY_SUM_RANKING);
		if (!X.isValid(list)) {
			return;
		}
		final Set<ZSetOperations.TypedTuple<String>> tuples = new HashSet<>(list.size());
		for (UserIntegralLog il : list) {
			tuples.add(ZSetOperations.TypedTuple.of(il.getUserId().toString(), il.getTradeAmount().doubleValue()));
		}
		stringRedisTemplate.opsForZSet().add(RedisKey.USER_INCOME_LAST_DAY_SUM_RANKING, tuples);
	}

	/**
	 * 获取用户通话收益
	 *
	 * @return left: 总收益
	 * right: 通话收益
	 */
	public UserIntegralIncomeDTO sumUserIncomeIntegralByInterval(Long userId, TimeInterval interval) {
		final QueryWrapper<UserIntegralLog> wrapper = new QueryWrapper<>();
		wrapper.eq(USER_ID, userId);
		wrapper.eq(FLOW, Flow.INCOME.value);
		wrapper.between(ADD_TIME, interval.getFinalBegin(), interval.getFinalEnd());
		return baseDao.sumUserIncome(wrapper);
	}

	/**
	 * 获取用户速配礼物收益
	 *
	 * @return left: 总收益
	 * right: 通话收益
	 */
	public UserIntegralIncomeDTO sumUserSpeedGiftIncome(Long userId, TimeInterval interval) {
		final QueryWrapper<UserIntegralLog> wrapper = new QueryWrapper<>();
		wrapper.eq("log." + USER_ID, userId);
		wrapper.eq("log." + FLOW, Flow.INCOME.value);
		wrapper.in("log." + COIN_TRADE_TYPE, COIN_FOR_GIFT, COIN_FOR_GIFT_BAG);
		wrapper.eq("log." + ASSOC_TYPE, "giftLog");
		wrapper.between("log." + ADD_TIME, interval.getFinalBegin(), interval.getFinalEnd());
		return baseDao.sumUserSpeedGiftIncome(wrapper);
	}

	/**
	 * 获取用户速配视频收益
	 *
	 * @return left: 总收益
	 * right: 通话收益
	 */
	public UserIntegralIncomeDTO sumUserSpeedVideoIncome(Long userId, TimeInterval interval) {
		final QueryWrapper<UserIntegralLog> wrapper = new QueryWrapper<>();
		wrapper.eq("log." + USER_ID, userId);
		wrapper.eq("log." + FLOW, Flow.INCOME.value);
		wrapper.eq("log." + COIN_TRADE_TYPE, COIN_FOR_VIDEO.name());
		wrapper.eq("log." + ASSOC_TYPE, "MediaLog");
		wrapper.between("log." + ADD_TIME, interval.getFinalBegin(), interval.getFinalEnd());
		return baseDao.sumUserSpeedVideoIncome(wrapper);
	}

	/**
	 * 获取代理总收益
	 */
	public BigDecimal sumAgentTotal(Long agentId) {
		final EasyDate easyDate = new EasyDate();
		Date endDate = easyDate.toDate();
		final TimeInterval interval = new TimeInterval(easyDate.addDay(-easyDate.getWeekDay()).toDate(), endDate);
		interval.setBeginCalendarField(Calendar.DATE);
		interval.setEndCalendarField(Calendar.DATE);
		return this.baseDao.findTotalAgentAmount(agentId, interval);
	}

}
